import { _decorator, Component, EditBox, EventMouse,PhysicsSystem,Node,resources,Prefab,instantiate, Vec3 ,geometry, Camera,Input,input, CameraComponent, MeshRenderer, SkeletalAnimation} from 'cc';
import { encode,decode } from "./msgpack-ts-master/src"
const { ccclass, property } = _decorator;

const _RECV_PAYLOAD_LENGTH = 1
const _RECV_PAYLOAD = 2

const CLIENTID_LENGTH = 16
const ENTITYID_LENGTH = 16
const SIZE_FIELD_SIZE = 4

const MT_INVALID = 0
// Server Messages
const MT_SET_GAME_ID = 1
const MT_SET_GATE_ID = 2
const MT_NOTIFY_CREATE_ENTITY = 3
const MT_NOTIFY_DESTROY_ENTITY = 4
const MT_DECLARE_SERVICE = 5
const MT_UNDECLARE_SERVICE = 6
const MT_CALL_ENTITY_METHOD = 7
const MT_CREATE_ENTITY_ANYWHERE = 8
const MT_LOAD_ENTITY_ANYWHERE = 9
const MT_NOTIFY_CLIENT_CONNECTED = 10
const MT_NOTIFY_CLIENT_DISCONNECTED = 11
const MT_CALL_ENTITY_METHOD_FROM_CLIENT = 12
const MT_SYNC_POSITION_YAW_FROM_CLIENT = 13
const MT_NOTIFY_ALL_GAMES_CONNECTED = 14
const MT_NOTIFY_GATE_DISCONNECTED = 15
const MT_START_FREEZE_GAME = 16
const MT_START_FREEZE_GAME_ACK = 17
// Message types for migrating
const MT_MIGRATE_REQUEST = 18
const MT_REAL_MIGRATE = 19


const MT_GATE_SERVICE_MSG_TYPE_START = 1000
const MT_REDIRECT_TO_GATEPROXY_MSG_TYPE_START = 1001 // messages that should be redirected to client proxy
const MT_CREATE_ENTITY_ON_CLIENT = 1002
const MT_DESTROY_ENTITY_ON_CLIENT = 1003
const MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT = 1004
const MT_NOTIFY_MAP_ATTR_DEL_ON_CLIENT = 1005
const MT_NOTIFY_LIST_ATTR_CHANGE_ON_CLIENT = 1006
const MT_NOTIFY_LIST_ATTR_POP_ON_CLIENT = 1007
const MT_NOTIFY_LIST_ATTR_APPEND_ON_CLIENT = 1008
const MT_CALL_ENTITY_METHOD_ON_CLIENT = 1009
const MT_UPDATE_POSITION_ON_CLIENT = 1010
const MT_UPDATE_YAW_ON_CLIENT = 1011
const MT_SET_CLIENTPROXY_FILTER_PROP = 1012
const MT_CLEAR_CLIENTPROXY_FILTER_PROPS = 1013


// add more ...

const MT_REDIRECT_TO_GATEPROXY_MSG_TYPE_STOP = 1500

const MT_CALL_FILTERED_CLIENTS = 1501
const MT_SYNC_POSITION_YAW_ON_CLIENTS = 1502

const speed = 6 

// add more ...

const MT_GATE_SERVICE_MSG_TYPE_STOP = 2000
export class ClientEntityComponent extends Component{
    ID:string;
    isPlayer=false;
    owner:NewComponent=null;
    typeName:string=null;
    attrs:{ [key: string]: any; } ={};
    main:NewComponent;
    view:Node;
    targetPos:Vec3
    create(owner:NewComponent, typeName:string, entityID:string, attrs:Uint8Array) {
        this.owner = owner 
        this.typeName = typeName
        this.ID = entityID
        this.attrs = attrs 
        this.isPlayer = false
    }
    onCreated(pos:Vec3) {
        if (this.typeName == "Account") {
            console.log("Account created, start logining...")//自己
        }else if(this.typeName=='__space__ '){
            //不知道是什么
        }
        else  if(this.typeName=='Player' || this.typeName=='Monster'){
            this.createEntityView(pos);
        }
    }    
    onBecomePlayer() {
        let scene = cc.director.getScene()
        
        console.log("获得玩家对象：", this, scene.name)
        if (this.typeName == "Avatar") {
            // 玩家登录成功！
            // this.getGoWorld().onAvatarLoginSuccess( this )
        } else if (this.typeName == "Account") {
            // 账号创建成功，可以开始登陆
            // if (scene.name != "login") {
            //     cc.director.loadScene("login");
            // }
        }
    }
    onCall(method, ...args:Uint8Array[]) {
        console.log(this+",onCall,"+method+"("+args+")")
        // this[method](...args)
        this[method](args)//20230929
    }
    ShowInfo(msg){
        console.info(msg)
    }
      // Put Client Methods Here 
    ShowError(msg:string) {
        // this.getGoWorld().showErrorTip(msg)
        console.error('ShowError'+msg);
    }  
    DisplayAttack(playerID:string):void {
		// ClientEntity player = GoWorld.GetEntity (playerID);
        let player = this.main.entities[playerID];
		console.warn (this + " attack " + playerID + " " + player);
		// Vector3 startPos = this.gameObject.transform.position;
		// Vector3 endPos = player.gameObject.transform.position;
		// startPos.y = 0.5f;
		// endPos.y = 0.5f;
		// lineRenderer.SetPosition(0, startPos);
		// lineRenderer.SetPosition (1, endPos);
		// lineRenderer.enabled = true;
		// attackTime = Time.time;
	}
    applyMapAttrChange(path:{[key:string]:any}, key:string, val:any) {
        let attr = this.getAttrByPath(path)
        var rootkey = path!=null&&path.length > 0 ? path[0] : key
        attr[key] = val 

        var methodName = 'onAttrChange_'+rootkey;
        console.log('methodName=',methodName);
        this[methodName]();
    }
    getAttrByPath(path:any): any {
        var attr = this.attrs
        if(path==null)
            return attr;

        for (var i=0; i< path.length;i++) {
            let key = path[i]
            attr = attr[key]
        }
        return attr 
    }
    onAttrChange_action(){
        console.log('onAttrChange_action,'+this.attrs['action']);
    }
    onAttrChange_hp(){
        let hp = this.attrs['hp'];
        console.log('onAttrChange_hp,'+hp);
        
        // var meshRenderer = this.view.getComponent(MeshRenderer);
        if(this.view != undefined)
        {
            this.view.scale=new Vec3(1,hp/10+1,1);
            if(0==hp)
            {
                var skeleton =this.view.getComponent(SkeletalAnimation);
                let animName = 'died'
                skeleton.getState(animName).repeatCount=1
                skeleton.play(animName)
            }
        }
        
    }
    onAttrChange_name(){
        console.log('onAttrChange_name,'+this.attrs['name']);
    }
    onAttrChange_lv(){
        console.log('onAttrChange_lv,'+this.attrs['lv']);
    }
    onAttrChange_hpmax(){
        console.log('onAttrChange_hpmax,'+this.attrs['hpmax']);
    }
    createEntityView(pos:Vec3)
    {
        // resources.load("Cube", Prefab, (err, prefab) => 
        resources.load("altman-blue", Prefab, (err, prefab) => 
        {
            
            console.log('resources.load callback',err,prefab);
            const newNode = instantiate(prefab);
            this.node.addChild(newNode);
            newNode.position = pos;//Vec3(-3,0,0);
            console.log('resources.load newNode',newNode);
            this.view=newNode;
        });
    }
    
    Move(deltaTime: number) {
        if(this.view == undefined || this.targetPos==undefined)
            return;

        if(Vec3.distance( this.view.position,this.targetPos)>1){
            let dir:Vec3=new Vec3()
            Vec3.subtract(dir,this.targetPos,this.view.position);
            Vec3.normalize(dir,dir);
            let newPos:Vec3=new Vec3()
            this.view.position = Vec3.scaleAndAdd(newPos,this.view.position,dir,deltaTime*speed)
            this.main.syncPositionYawFromClient(this.ID,this.view.position.x,this.view.position.y,this.view.position.z,0)
        }
    }
}
@ccclass('NewComponent')
export class NewComponent extends Component {
    ID="ID_TEST";
    // entities:Record<string, string>;
    recvBuf = new ArrayBuffer(0);
    recvStatus = _RECV_PAYLOAD_LENGTH;
    recvPayloadLen = 0;
    entities : { [key: string]: ClientEntityComponent; } ={};
    sendBuf = new ArrayBuffer(1024*1024);
    _sendPacket = new DataView(this.sendBuf);
    _sendPacketWritePos = SIZE_FIELD_SIZE;
    player:ClientEntityComponent=null;
    editBox:EditBox=null;
    plane:Node;
    camera:CameraComponent
    start() {
        let main = this;
        this.plane=this.node.parent.getChildByName("Plane");
        this.camera=this.node.parent.getChildByName("Main Camera").getComponent(CameraComponent);
        input.on(Input.EventType.MOUSE_UP, this.onMouseUp, this);


 
        
        
        // this.recvBuf = new ArrayBuffer()
        // this.recvStatus = _RECV_PAYLOAD_LENGTH
        // this.recvPayloadLen = 0
        // this.entities = {}
        // this.sendBuf = new ArrayBuffer(1024*1024);
        // this._sendPacket = new DataView(this.sendBuf);
        // this._sendPacketWritePos = SIZE_FIELD_SIZE
        this.editBox = this.node.getChildByName("EditBox").getComponent(EditBox);
        // this.editBox.string = 'asdf';
        this.connect();
    }

    update(deltaTime: number) {
        this.player?.Move(deltaTime)
    }

    onMouseUp(eventMouse: EventMouse) {
    //     if (event.getButton() === 0) {
    //         this.jumpByStep(1);
    //     } else if (event.getButton() === 2) {
    //         this.jumpByStep(2);
    //     }

    // }
        let ray = new geometry.Ray();
        this.camera.screenPointToRay(eventMouse.getLocationX(), eventMouse.getLocationY(), ray);
        // 以下参数可选
        const mask = 0xffffffff;
        const maxDistance = 10000000;
        const queryTrigger = true;

        console.log( eventMouse );
        console.log( ray );
        if (PhysicsSystem.instance.raycastClosest(ray)){//}, mask, maxDistance, queryTrigger)) {
            const raycastClosestResult = PhysicsSystem.instance.raycastClosestResult;
            const hitPoint = raycastClosestResult.hitPoint
            const hitNormal = raycastClosestResult.hitNormal;
            const collider = raycastClosestResult.collider;
            const distance = raycastClosestResult.distance;      
            console.log( collider.node.name , hitPoint );
            // this.callServer(this.player,"move",)
            // this.player.node.position=hitPoint;
            this.player.targetPos=hitPoint;
        }

    }
    syncPositionYawFromClient(entityID:string,x: number, y:number, z:number, yaw:number):void
    {
        // Packet pkt = new Packet(Proto.MT_SYNC_POSITION_YAW_FROM_CLIENT);
        // pkt.AppendEntityID(entityID);
        // pkt.AppendFloat32(x);
        // pkt.AppendFloat32(y);
        // pkt.AppendFloat32(z);
        // pkt.AppendFloat32(yaw);
        // this.sendPacket(pkt);
        this.appendUint16(MT_SYNC_POSITION_YAW_FROM_CLIENT)
        this.appendEntityID(entityID)
        this.appendFloat32(x)
        this.appendFloat32(y)
        this.appendFloat32(z)
        this.appendFloat32(yaw)
        this.sendPacket()
    }
    onBtnClick(event, customEventData){
        // var editBox = this.node.getChildByName("EditBox").getComponent(EditBox);
        console.log('按钮按下,edit='+this.editBox.string);
        let account = this.getEntityByType("Account")
        this.callServer(account,"Register",this.editBox.string,"asdf");
    }    
    onLogin() {
        // let loginUser = cc.find("loginUser").getComponent("cc.EditBox").string
        // let loginPwd = cc.find("loginPwd").getComponent("cc.EditBox").string
        console.log("登录...", this.editBox.string, "asdf" )

        let account = this.getEntityByType("Account")
        console.log("account", account !== null ? account.toString():null)
        if (account === null) {
            console.error("正在连接服务器，请耐心等待")
            return 
        }
        
        // if (loginUser == "" || loginPwd == "") {
        //     console.error("请输入用户名和密码！")
        //     return 
        // }

        this.callServer(account,"Login", this.editBox.string,  "asdf")
    }
    getEntityByType(typeName) {
        for (var eid in this.entities) {
            let e = this.entities[eid]
            if (e.typeName == typeName) {
                return e
            }
        }
        return null 
    }
    callServer(entity:ClientEntityComponent, method , ...args2) {
        var args = Array.prototype.slice.call(arguments);
        args = args.slice(2)
        this.callServerMethod( entity, method, args )
    }
    callServerMethod(entity:ClientEntityComponent, method:string, args) {
        console.log(">>> "+entity+"."+method+"("+args+")")
        // 	packet.AppendUint16(MT_CALL_ENTITY_METHOD_FROM_CLIENT)
        // 	packet.AppendEntityID(id)
        // 	packet.AppendVarStr(method)
        // 	packet.AppendArgs(args)

        this.appendUint16(MT_CALL_ENTITY_METHOD_FROM_CLIENT)
        this.appendEntityID(entity.ID)
        this.appendVarStr(method)
        this.appendArgs(args)
        this.sendPacket()
    }
    appendUint16(v) {
        this._sendPacket.setUint16(this._sendPacketWritePos, v, true)
        this._sendPacketWritePos += 2
    }
    appendEntityID(eid) {
        console.log(eid);
        let b = this.string2Uint8Array(eid)
        console.log("convert", eid, "to", b, b.length)
        this.appendBytes(b)
    }
    string2Uint8Array(str) {
        console.log(str);
        let bufView = new Uint8Array(str.length);
        for (var i=0, strLen=str.length; i<strLen; i++) {
          bufView[i] = str.charCodeAt(i);
        }
        return bufView;
    }
    appendBytes(b) {
        new Uint8Array(this._sendPacket.buffer, this._sendPacketWritePos, b.length).set(b, 0);  
        this._sendPacketWritePos += b.length
    }  
    appendVarStr(s) {
        let b = this.string2Uint8Array(s)
        this.appendVarBytes(b)
    }
    appendVarBytes(b) {
        this.appendUint32(b.length)
        this.appendBytes(b)
    }
    appendUint32(v) {
        this._sendPacket.setUint32(this._sendPacketWritePos, v, true)
        this._sendPacketWritePos += 4
    }    
    
    appendFloat32(v:number):void {
        this._sendPacket.setFloat32(this._sendPacketWritePos, v, true)
        this._sendPacketWritePos += 4
    }  
    appendArgs(args) {
        console.log("appendArgs", args.length, args)
        this.appendUint16(args.length)
        for (var i=0; i<args.length;i++) {
            this.appendData(args[i])
        }
    }
    appendData(data) {
        data = encode(data)
        console.log("msgpack encode:", typeof(data), data.length)
        this.appendVarBytes(data)
    }
    sendPacket() {
        let payloadLen = this._sendPacketWritePos - SIZE_FIELD_SIZE
        this._sendPacket.setUint32(0, payloadLen, true)
        let packetLen = this._sendPacketWritePos
        this._sendPacketWritePos = SIZE_FIELD_SIZE
        console.log("sendPacket:", packetLen)
        this.websocket.send(this.sendBuf.slice(0, packetLen))
    }
    websocket : WebSocket = null;
    connect() {
        var serverAddr='localhost';
        var serverPort=15101;
        var serverAddr = 'ws://'+serverAddr+':'+serverPort+'/ws'
        console.log("正在连接 " + serverAddr + ' ...')
        var websocket = new WebSocket(serverAddr)
        this.websocket = websocket
        
        websocket.binaryType = 'arraybuffer'
        console.log(websocket)
        var gameclient = this

          //连接发生错误的回调方法
          websocket.onerror = function () {
               console.log("WebSocket连接发生错误");
          };

           //连接成功建立的回调方法
           websocket.onopen = function () {
               console.log("WebSocket连接成功");
           }

          //接收到消息的回调方法
           websocket.onmessage = function (event) {
               var data = event.data
               console.log("收到数据：", typeof(data), data.length);
               gameclient.onRecvData(data)
          }

           //连接关闭的回调方法
           websocket.onclose = function () {
              console.log("WebSocket连接关闭");
           }

           //监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，server端会抛异常。
           window.onbeforeunload = function () {
               console.log("onbeforeunload");
           }
    }
    onRecvData(data) {
        if (this.recvBuf.byteLength == 0) {
            this.recvBuf = data
        } else {
            var tmp = new Uint8Array( this.recvBuf.byteLength + data.byteLength );
            tmp.set( new Uint8Array( this.recvBuf ), 0 );
            tmp.set( new Uint8Array( data ), this.recvBuf.byteLength );
            this.recvBuf = tmp.buffer
        }

        console.log("未处理数据：", this.recvBuf.byteLength)
        while (true) {
            let payload = this.tryReceivePacket()
            if (payload !== null) {
                this.onReceivePacket(payload)
            } else {
                break 
            }
        }
    }
     // 从已经收到的数据（recvBuf）里解析出数据包（Packet）
     tryReceivePacket() {
        let recvBufView = new DataView(this.recvBuf)
        if (this.recvStatus == _RECV_PAYLOAD_LENGTH) {
            if (this.recvBuf.byteLength < SIZE_FIELD_SIZE) {
                return null
            }
            this.recvPayloadLen = recvBufView.getUint32(0, true)
            console.log("数据包大小: ", this.recvPayloadLen)
            this.recvStatus = _RECV_PAYLOAD
        }

        // recv status == _RECV_PAYLOAD
        console.log("包大小：", this.recvPayloadLen, "现有数据：", this.recvBuf.byteLength - SIZE_FIELD_SIZE)
        if (this.recvBuf.byteLength - SIZE_FIELD_SIZE < this.recvPayloadLen) {
            // payload not enough
            return null
        }

        // 足够了，返回包数据
        var payload = this.recvBuf.slice(SIZE_FIELD_SIZE, SIZE_FIELD_SIZE+this.recvPayloadLen)
        this.recvBuf = this.recvBuf.slice(SIZE_FIELD_SIZE+this.recvPayloadLen)
        // 恢复到接收长度状态
        this.recvStatus = _RECV_PAYLOAD_LENGTH
        this.recvPayloadLen = 0
        return payload
    }

    onReceivePacket (payload2) {
        // payload is ArrayBuffer
        payload = new DataView(payload2) // 转换为DataView便于操作
        var [msgtype, payload] = this.readUint16(payload)
        console.log("收到包：", payload, payload.byteLength, "，消息类型：", msgtype)
        if (msgtype != MT_CALL_FILTERED_CLIENTS && msgtype != MT_SYNC_POSITION_YAW_ON_CLIENTS) {
            var [dummy, payload] = this.readUint16(payload)
            console.log("gateid", dummy)
            var [dummy2, payload] = this.readBytes(payload, CLIENTID_LENGTH) // read ClientID
            console.log("clientid", dummy2.length)
        }

        if (msgtype == MT_CREATE_ENTITY_ON_CLIENT) {
            this.handleCreateEntityOnClient(payload)
        } else if (msgtype == MT_CALL_ENTITY_METHOD_ON_CLIENT) {
            this.handleCallEntityMethodOnClient(payload)
        } else if (msgtype == MT_DESTROY_ENTITY_ON_CLIENT) {
            this.handleDestroyEntityOnClient(payload)
        } else if (msgtype == MT_CALL_FILTERED_CLIENTS) {
            this.handleCallFilteredClients(payload)
        } else if (msgtype == MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT) {
            this.handleNotifyMapAttrChangeOnClient(payload)
        } else if (msgtype == MT_NOTIFY_MAP_ATTR_DEL_ON_CLIENT) {

        } else if (msgtype == MT_NOTIFY_LIST_ATTR_APPEND_ON_CLIENT) {

        } else if (msgtype == MT_NOTIFY_LIST_ATTR_CHANGE_ON_CLIENT) {

        } else if (msgtype == MT_NOTIFY_LIST_ATTR_POP_ON_CLIENT) {
        } else if (msgtype == MT_SYNC_POSITION_YAW_ON_CLIENTS) {
            this.handleSyncPositionYawOnClients(payload)
        } else {
            console.error("无法识别的消息类型："+msgtype)
        }
    }
    handleSyncPositionYawOnClients(payload:DataView):void
    {
        while(0 < payload.byteLength)// while (pkt.UnreadPayloadLen > 0)
        {
            console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
        
            var [entityID, payload] = this.readEntityID(payload)// string entityID = pkt.ReadEntityID();
            // float x = pkt.ReadFloat32();
            // float y = pkt.ReadFloat32();
            // float z = pkt.ReadFloat32();
            // float yaw = pkt.ReadFloat32();
            var [x, payload] = this.readFloat32(payload)
            console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
            var [y, payload] = this.readFloat32(payload)
            console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
            var [z, payload] = this.readFloat32(payload)
            console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
            var [yaw, payload] = this.readFloat32(payload)
            console.log('payload.byteOffset,payload.byteLength',payload.byteOffset,payload.byteLength)
            // EntityManager.Instance.OnSyncEntityInfo(entityID, x, y, z, yaw);
            console.log('handleSyncPositionYawOnClients',entityID,x,y,z,yaw)
            
            var entity = this.entities[entityID];
            console.log(entity,entity.view)
            if(entity.view != undefined)
                entity.view.position=new Vec3(x,y,z);
        }
    }
    handleNotifyMapAttrChangeOnClient(payload:DataView) {
        var [entityID, payload] = this.readEntityID(payload)
        var [path, payload] = this.readData(payload)
        var [key, payload] = this.readVarStr(payload)
        var [val, payload] = this.readData(payload)
        console.log("MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT", entityID, "path", typeof(path), JSON.stringify(path), "key", key, "val", val)
        // console.log("MT_NOTIFY_MAP_ATTR_CHANGE_ON_CLIENT", entityID, "path", typeof(path), JSON.stringify(path), path.length, "key", key, "val", val)

        let e = this.entities[entityID]
        if (!e) {
            console.log("找不到对象："+entityID)
            return  
        }

        e.applyMapAttrChange( path, key, val )
    }
    readUint8(buf:DataView):[number,DataView] {
        let v = buf.getUint8(0)
        return [v, new DataView(buf.buffer, buf.byteOffset+1)]
    }
    readUint16(buf:DataView):[number,DataView] {
        let v = buf.getUint16(0, true)
        return [v, new DataView(buf.buffer, buf.byteOffset+2)]
    }
    readUint32(buf:DataView) :[number,DataView]{
        let v = buf.getUint32(0, true)
        return [v, new DataView(buf.buffer, buf.byteOffset+4)]
    }
    readFloat32(buf:DataView) :[number,DataView]{
        let v = buf.getFloat32(0, true)
        return [v, new DataView(buf.buffer, buf.byteOffset+4)]
    }
    readBytes(buf:DataView, length) :[Uint8Array,DataView]{
        let v = new Uint8Array(buf.buffer, buf.byteOffset, length)
        return [v, new DataView(buf.buffer, buf.byteOffset+length)]
    }
    readVarBytes(buf:DataView) :[Uint8Array,DataView]{
        var [n, buf] = this.readUint32(buf)
        var [b, buf] = this.readBytes(buf, n)
        console.log('VarBytes len', n, 'b', b.length)
        return [b, buf]
    }
    readEntityID(buf:DataView) :[string,DataView]{
        var [eid, buf] = this.readBytes(buf, ENTITYID_LENGTH)
        var eid2 = this.uint8Array2String(eid)
        return [eid2, buf]
    }
    readVarStr(buf:DataView) :[any,DataView]{
        var [b, buf] = this.readVarBytes(buf)
        let s = this.uint8Array2String(b)
        return [s, buf]
    }
    readBool(buf):[any,DataView] {
        var b
        [b, buf] = this.readUint8(buf)
        b = b == 0 ? false : true
        return [b, buf]
    }
    readData(buf:DataView):[any,DataView] {
        var [b, buf] = this.readVarBytes(buf)
        let data = decode(b)//二进制转对象（带具体类型），反序列化，功能很强  20230929
        return [data, buf]
        // return [b, buf]
    }
    readArgs(buf:DataView) :[any[],DataView]{
        var [argcount, buf] = this.readUint16(buf)
        console.log("readArgs: argcount", argcount)
        var args = new Array<any>(argcount)
        for (var i = 0; i<argcount; i++) {
            var [data, buf] = this.readData(buf)
            args[i] = data
        }
        return [args, buf]
    }

    handleCreateEntityOnClient(payload:DataView) {
        var [isPlayer, payload] = this.readBool(payload)
        var [eid, payload] = this.readEntityID(payload)
        var [typeName, payload] = this.readVarStr(payload)
        var [x, payload] = this.readFloat32(payload)
        var [y, payload] = this.readFloat32(payload)
        var [z, payload] = this.readFloat32(payload)
        var [yaw, payload] = this.readFloat32(payload)
        var [clientData,payload] = this.readVarBytes(payload)
        clientData = decode(clientData)
        console.log("MT_CREATE_ENTITY_ON_CLIENT", "isPlayer", isPlayer, 'eid', eid,"typeName", typeName, 'position', x, y, z, 'yaw', yaw, 'clientData', JSON.stringify(clientData))
        
        // var e = new ClientEntity()
        let newNode = new Node();
        let cec = newNode.addComponent(ClientEntityComponent);
        cec.main=this;
        cec.create( this, typeName, eid, clientData )
        this.entities[eid] = cec;
        
        this.node.parent.addChild(newNode);
        if (isPlayer) {
            cec.isPlayer = true

            if (this.player) {
                // dupliate player!!!
                console.error("玩家对象重复：老玩家"+this.player.toString() + "，新玩家：", cec.toString())
            }

            this.player = cec
        }
        this.onEntityCreated(cec)
        cec.onCreated(new Vec3(x,y,z))

        if (this.player === cec) {
            cec.onBecomePlayer()
        }
}
onEntityCreated(e) {
    console.log("entity created:", e)
}

uint8Array2String(b) :string{
    return String.fromCharCode.apply(null, b)
}

handleDestroyEntityOnClient(payload:DataView) {
    // typeName := packet.ReadVarStr()
    // entityID := packet.ReadEntityID()
    var [typeName, payload] = this.readVarStr(payload)
    var [entityID, payload] = this.readEntityID(payload)
    let e = this.entities[entityID]
    if (e == undefined) {
        return 
    }
    
    if(e.view != undefined)
        e.view.destroy();
    
    delete this.entities[entityID]
    // this.entities.erased(entityID);

    if (this.player === e) {
        this.player = null
        console.log("失去玩家对象：", e.toString()) 
    }
}

handleCallEntityMethodOnClient(payload:DataView) {        
    // entityID := packet.ReadEntityID()
    // method := packet.ReadVarStr()
    // args := packet.ReadArgs()
    var [entityID, payload] = this.readEntityID(payload)
    var [method, payload] = this.readVarStr(payload)
    var [args, payload] = this.readArgs(payload)
    console.log("MT_CALL_ENTITY_METHOD_ON_CLIENT", "entityID", entityID, "method", method, "args", args.length, args)
    let e = this.entities[entityID]
    if (e == undefined) {
        console.log("找不到entity：", entityID)
        return 
    }
    e.onCall( method, ...args )
}

handleCallFilteredClients(payload:DataView) {
    var [fkey, payload] = this.readVarStr(payload)
    var [fval, payload] = this.readVarStr(payload)
    var [method, payload] = this.readVarStr(payload)
    var [args, payload] = this.readArgs(payload)
    console.log("MT_CALL_FILTERED_CLIENTS", fkey, "=", fval, "method=", method, "args=", args)

    this.player.onCall( method, ...args )
}
}


